using System;
using System.Collections.Generic;
using Arcane.Source.Utils;
using Godot;

namespace Arcane.Source.Client;

public class EventStorageHub
{
    private readonly Dictionary<Type, Entry> storages = [];

    public EventStorage<C, E> GetOrCreate<C, E>() where E: IDeferedEvent<C>
    {
        if (TryGet<C, E>(out var storage))
        {
            return storage;
        }
        else
        {
            storage = new();
            Add(storage);
            return storage;
        }
    }

    public void Add<C, E>(EventStorage<C, E> storage) where E : IDeferedEvent<C>
    {
        storages.Add(typeof(C), Entry.Create(storage));
    }

    public void SendEvent<C, E>(E ev, int? deferTicks = null) where E : IDeferedEvent<C>
    {
        if (TryGet<C, E>(out var storage))
        {
            storage.Push(ev, deferTicks);
        }
    }

    public EventStorage<C, E> Get<C, E>() where E : IDeferedEvent<C>
    {
        return storages.TryGetValue(typeof(C), out var entry) && entry.TryGet<C, E>(out var storage) ? storage : null;
    }

    public bool TryGet<C, E>(out EventStorage<C, E> storage) where E: IDeferedEvent<C>
    {
        storage = Get<C, E>();
        return storage != null;
    }

    class Entry
    {
        private Type _context;
        private Type _ev;

        private object _storage;

        public static Entry Create<C, E>(EventStorage<C, E> storage) where E: IDeferedEvent<C>
        {
            return new()
            {
                _context = typeof(C),
                _ev = typeof(E),
                _storage = storage,
            };
        }

        public bool TryGet<C, E>(out EventStorage<C, E> storage) where E: IDeferedEvent<C>
        {
            if (_context == typeof(C) && _ev == typeof(E))
            {
                storage = (EventStorage<C, E>)_storage;
                return true;
            }
            storage = null;
            return false;
        }
    }
}